-- Parsec
-- Module 'util'
require "snapshots"

function gamefile_(fl)
  return function()
    gamefile("game/"..fl, true);
  end
end

function set_checkpoint()
  make_snapshot(1); 
end

function load_checkpoint()
  return restore_snapshot(1);
end

function reset_state(scn)
  if type(scn) ~= "table" then
    scn = ref(scn);
  end
  for k,v in pairs(scn) do
    if string.char(k:byte(1)) == "_" then
      if v == true or v == false then
        scn[k] = false;
      else
        scn[k] = 0;
      end
    end
  end
end

copy_fields = { 
  "Exam", "Open", "Move", "Ascend", "Wreck", "Approach", "Walk", "Stand", "Sit", "Lie", "Attack", "Talk", "Listen", 
  "Drink", "Descend", "Activate", "TurnOn", "TurnOff", "Close", "Search", "Put", "Throw", "Take", "Tear", "Shoot",
  "Crack", "Use", "Hide", "Request", "Call", "Write", "Read", "Reply", "Photo", "Wear", "TakeOff", "Give", "Fetch",
  "Choose", "Insert", "Look", "ListenTo", "Pure", "Pounce", "Kick", "Spin", "Stir", "Press", "Enter", "TearOff",
  "PutAway", "PutIn", "TakeFrom", "Bant", "Plug", "Reboot", "Think", "Film", "Invent", "Threat", "Draw", "Spy",
  "before_Walk", "before_Approach", "before_Move", "before_Search",
  "live", "weight", "inrange", "kind_",
  "dsc", "articles", "withPure", "withThrow", "withPutAway", "withPutIn", "withPut", "withShoot", "withOpen",
  "withGive", "withFetch", "withCrack", "withTakeFrom", "withTakeOff", "withTearOff", "withThreat", "withTakeForce"
};
function from_(obj)
  return function(tab)
    if type(obj) ~= "table" then
      obj = ref(obj);
    end
    local no = {};
    for _,k in pairs(copy_fields) do
      no[k] = obj[k];
    end    
    for k,v in pairs(tab) do
      if k == "articles" then
        local art = no[k];
        local cart = {};
        for ok,ov in pairs(art) do
          cart[ok] = ov;
        end
        if tab[k] ~= nil then
          for kk,vv in pairs(tab[k]) do
            cart[kk] = vv;
          end
        end
        no[k] = cart;
      elseif v == nild then
        no[k] = nil;
      else
        no[k] = v;
      end
    end
    if not tab.permanent then
      no.permanent = nil;
    end
    return item(no);
  end
end

function concat_(tab)
  return function(s)
    local str = "";
    for _,v in ipairs(tab) do
      if type(v) == "function" then
        v = rcall(v,s);
      end
      if v ~= nil then
        str = str.." "..v;
      end
    end
    return str;
  end
end

function string_(s)
  return function(str)
    return function()
      if type(s) == "function" then
        s = s();
      end
      return str:replace("##", s);
    end
  end
end

function bg_(file)
  return function()
    theme.set("scr.gfx.bg", "gfx/"..file..".jpg");
  end
end

function rnd_(tab)
  return function()
    local len = table.len(tab);
    local r = math.ceil(rnd(len*100)/100);
    return tab[r];
  end
end

function if_(c,data)
  return function(this)
    local res = apply(c,this);
    if data == nil then
      return res;
    elseif res then
      return rcall(data,this);
    end
  end
end

function not_(c)
  return function(this)
    return not apply(c,this);
  end
end

function walk_(scn,dsc)
  return function(s)
    cls();
    if dsc ~= nil then
      ref(scn)._tmpdsc = rcall(dsc,s);
    end
    walk(scn);
  end
end

function walkfast_(scn)
  return function()
    _nofading = true;
    walk(scn);
  end
end

function walkfast2_(scn,dsc)
  return function(s)
    cls();
    if dsc ~= nil then
      ref(scn)._tmpdsc = rcall(dsc,s);
    end
    _nofading = true;
    walk(scn);
  end
end

function walkout_(scn)
  return function()
    _nofading = true;
    walkout(scn);
  end
end

function take_(o)
  return function()
    take(o);
  end
end

function takeself(s)
  take(s);
end
function dropself(s)
  remove(s,me());
end
function removeself(s,scn)
  remove(s,scn);
end
function putself(s,scn)
  put(s,scn);
end
function putself_(scn)
  return function(s)
    put(s,scn);
  end
end

function drop_(o)
  return function()
    remove(o,me());
  end
end

function remove_(o,scn)
  return function()
    remove(o,scn);
  end
end

function put_(o,scn)
  return function()
    put(o,scn);
  end
end

function inc_(var)
  return function(this)
    exec(var.."="..var.."+1",this);
  end
end

function setnum_(var,num)
  return function(this)
    exec(var.."="..num,this);
  end
end

function nil_(var)
  return function()
    local hw = here();
    hw[var] = nil;
  end
end

function set_(var)
  return function(this)
    exec(var.."=true",this);
  end
end

function unset_(var)
  return function(this)
    exec(var.."=false",this);
  end
end

function choose_(tab)
  return function(s,w,t)
    return generic_choose(s, w, t, tab, apply);
  end
end

function chooset_(tab)
  return function(s,w,t)
    return generic_choose(s, w, t, tab, function(s)
      if not t then
        return s == "nil";
      else
        return s == deref(t);
      end
    end);
  end
end

function unless_(cnd,res)
  return function(tab)
    return function(s,w,t)
      if apply(cnd) then
        return rcall(res);
      elseif type(tab) == "table" then
        return generic_choose(s, w, t, tab, apply);
      else
        return rcall(tab,s);
      end
    end
  end
end

function use_(tab) 
  return function(s,w,t)
    return generic_choose(s, w, t, tab, function(s)
      if not w then
        return s == "nil";
      else
        return s == deref(w);
      end
    end);
  end
end

function loc(...)
  local hs = deref(here());
  local arg = {...}
  for i,v in ipairs(arg) do
    if (type(v) == "table" and here() == v) or hs == v then
      return true;
    end
  end
end

function location_(tab)
  return function(s,w,t)
    return generic_choose(s, w, t, tab, function(s) return loc(s) end);
  end
end

function generic_choose(s, w, t, tab, fun)
  local df,ret = nil;
  local found = false;
  for i,v in pairs(tab) do
    if i == "default" then
      df = v;
    elseif fun(i,s) then
      found = true;
      ret = rcall(v,s,w,t);
      break;
    end
  end
  if not found and df ~= nil then
    ret = rcall(df,s,w,t);
  end
  if ret ~= nil and type(ret) == "string" then
    ret = ret:expand(s)
  end
  return ret;
end

function name_(tab)
  return function(s,w,t)
    return generic_choose(s,w,t,tab, function(s) 
      return get_verb(parser.verbnr)==s; 
    end);
  end
end

function word_(tab)
  return function(s,w,t)
    return generic_choose(s,w,t,tab, function(s) return parser:word(1)==s end);
  end
end

function chain_(tab)
  return function(s,w,t)
    local ret = nil;
    local str = nil;
    for i,v in ipairs(tab) do
      if type(v) == "function" then
        ret = rcall(v,s,w,t);
      else
        ret = v;
      end
      if type(ret) == "string" then
        ret = ret:expand(s);
        str = ret;
      end
    end
    if ret == nil then
      ret = str;
    end
    return ret;
  end
end

function table.len(self)
  local count = 0;
  for i,v in pairs(self) do 
    count = count + 1;
  end
  return count;
end

local function make_tab(tab)
  local a = tab;
  if type(tab) == "table" then
    a = chain_(tab);
  end
  return a;
end

function takeit_(tab)
  return chain_ {
    takeself,
    make_tab(tab)
  }
end
